/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/


/*
 * NAME
 *	workpool.c
 *
 * DESCRIPTION
 *	This file contains the private data definitions and code for the ray
 *	job work pool.	Each processor has its own workpool.
 *
 *	The workpool consists of a stack of pixel bundles.  Each bundle
 *	contains jobs for primary rays for a contiguous 2D pixel screen
 *	region.
 *
 */


#include <stdio.h>
#include <math.h>
#include "rt.h"



/*
 * NAME
 *	PutJob - put another job into pid's work pool
 *
 * SYNOPSIS
 *	VOID	PutJob(xs, ys, xe, ye, xbe, ybe, pid)
 *	INT	xs,  ys;		// Start of block.
 *	INT	xe,  ye;		// Extent of block.
 *	INT	xbe, ybe;		// Extent of bundle.
 *	INT	pid;			// Process id.
 *
 *  DESCRIPTION
 *	Given a block of image screen pixels, this routine generates pixel
 *	bundle entries that are inserted into pid's work pool stack.
 *
 *	A block includes the starting pixel address, the block size in x and y
 *	dimensions and the bundle size for making pixel jobs.
 *
 *	Pixel addresses are 0 relative.
 *
 *	Locking of workpools for job insertion is not required since a given
 *	process only inserts into its own pool and since we use a {
}
 *	before jobs can be removed from workpools.
 *
 * RETURNS
 *	Nothing.
 */

VOID	PutJob(INT xs, INT ys, INT xe, INT ye, INT xbe, INT ybe, INT pid)
{
    INT	i, j;
    INT	xb_addr, yb_addr;		/* Bundle pixel address.     */
    INT	xb_end,  yb_end;		/* End bundle pixels.	     */
    INT	xb_size, yb_size;		/* Bundle size. 	     */
    WPJOB	*wpentry;			/* New work pool entry.      */

    /* Starting block pixel address (upper left pixel). */

    xb_addr = xs;
    yb_addr = ys;

    /* Ending block pixel address (lower right pixel). */

    xb_end = xb_addr + xe - 1;
    yb_end = yb_addr + ye - 1;

    for (i = 0; i < ye; i += ybe)
    {
        for (j = 0; j < xe; j += xbe)
        {
            /* Determine bundle size. */

            if ((xb_addr + xbe - 1) <= xb_end)
                xb_size = xbe;
            else
                xb_size = xb_end - xb_addr + 1;

            if ((yb_addr + ybe - 1) <= yb_end)
                yb_size = ybe;
            else
                yb_size = yb_end - yb_addr + 1;


            /* Initialize work pool entry. */

            wpentry = (WPJOB*)GlobalMalloc(sizeof(WPJOB), "workpool.c");

            wpentry->xpix = xb_addr;
            wpentry->ypix = yb_addr;
            wpentry->xdim = xb_size;
            wpentry->ydim = yb_size;


            /* Add to top of work pool stack. */

            if (!gm->workpool[pid][0])
                wpentry->next = NULL;
            else
                wpentry->next = gm->workpool[pid][0];

            gm->workpool[pid][0] = wpentry;
            xb_addr += xbe;
        }

        xb_addr  = xs;
        yb_addr += ybe;
    }
}



/*
 * NAME
 *	GetJob - get next job from pid's work pool
 *
 * SYNOPSIS
 *	INT	GetJob(job, pid)
 *	RAYJOB	*job;			// Ray job description.
 *	INT	pid;			// Process id.
 *
 * DESCRIPTION
 *	Return a primary ray job bundle from the top of the stack.  A ray job
 *	bundle consists of the starting primary ray pixel address and the size
 *	of the pixel bundle.
 *
 * RETURNS
 *	Work pool status code.
 */

INT	GetJob(RAYJOB *job, INT pid)
{
    WPJOB	*wpentry;			/* Work pool entry.	     */

    wpentry = gm->workpool[pid][0];

    if (!wpentry)
    {
        gm->wpstat[pid][0] = WPS_EMPTY;
        return (WPS_EMPTY);
    }

    gm->workpool[pid][0] = wpentry->next;

    /* Set up ray job information. */

    job->x	   = wpentry->xpix;
    job->y	   = wpentry->ypix;
    job->xcurr = wpentry->xpix;
    job->ycurr = wpentry->ypix;
    job->xlen  = wpentry->xdim;
    job->ylen  = wpentry->ydim;

    GlobalFree(wpentry);
    return (WPS_VALID);
}



/*
 * NAME
 *	GetJobs - get next job for pid (with job stealing)
 *
 * SYNOPSIS
 *	INT	GetJobs(job, pid)
 *	RAYJOB	*job;			// Ray job pointer from work pool.
 *	INT	pid;			// Process id.
 *
 * DESCRIPTION
 *
 * RETURNS
 *	Workpool status.
 */

INT	GetJobs(RAYJOB *job, INT pid)
{
    INT	i;

    i = pid;

    /* First, try to get job from pid's own pool (or pool 0). */

    if (gm->wpstat[i][0] == WPS_VALID)
        if (GetJob(job, i) == WPS_VALID)
        {
            return (WPS_VALID);
        }


    /*
     *	If that failed, try to get job from another pid's work
     *	pool.
     */

    i = (pid + 1) % gm->nprocs;
printf("pool is empty\n");
    while (i != pid)
    {
        if (gm->wpstat[i][0] == WPS_VALID)
            if (GetJob(job, i) == WPS_VALID)
            {
                return (WPS_VALID);
            }

        i = (i + 1) % gm->nprocs;
    }

    return (WPS_EMPTY);
}



/*
 * NAME
 *	PrintWorkPool - print out the work pool entries for a given process
 *
 * SYNOPSIS
 *	VOID	PrintWorkPool(pid)
 *	INT	pid;			// Process id.
 *
 * RETURNS
 *	Nothing.
 */

VOID	PrintWorkPool(INT pid)
{
    WPJOB	*j;

    j = gm->workpool[pid][0];

    while (j)
    {
        printf("Workpool entry:    pid=%3ld    xs=%3ld    ys=%3ld    xe=%3ld    ye=%3ld\n", pid, j->xpix, j->ypix, j->xdim, j->ydim);
        j = j->next;
    }
}



/*
 * NAME
 *	InitWorkPool - fill pid's work pool with jobs
 *
 * SYNOPSIS
 *	VOID	InitWorkPool(pid)
 *	INT	pid;			// Process id to initialize.
 *
 * RETURNS
 *	Nothing.
 */

VOID	InitWorkPool(INT pid)
{
    INT	i;
    INT	x, y;
    INT	xe, ye;
    INT	xsize, ysize;

    gm->wpstat[pid][0]   = WPS_VALID;
    gm->workpool[pid][0] = NULL;

    i      = 0;
    xsize  = Display.xres/blockx;
    ysize  = Display.yres/blocky;

    for (y = 0; y < Display.yres; y += ysize)
    {
        if (y + ysize > Display.yres)
            ye = Display.yres - y;
        else
            ye = ysize;

        for (x = 0; x < Display.xres; x += xsize)
        {
            if (x + xsize > Display.xres)
                xe = Display.xres - x;
            else
                xe = xsize;

            if (i == pid)
                PutJob(x, y, xe, ye, bundlex, bundley, pid);

            i = (i + 1)%gm->nprocs;
        }
    }

}

